package cn.mong.util.modelexcel;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import cn.mong.util.modelexcel.CellStyleCustomizer.DefaultCellStyleCustomizer;
import cn.mong.util.modelexcel.ModelToCellsConverter.DefaultModelToCellsConverter;
import lombok.extern.slf4j.Slf4j;

/**
 * 用于解析注解模型
 * 
 * @author mengxianming@2021-9-15 9:48:12
 *
 * @param <D>
 */
@Slf4j
public class AnnotationModelHeaderResolver{

    private Class<?> modelClazz;
    private List<HeaderCell> flatHeaders;

    private List<HeaderCell> hierarchyHeaders;

    private int totalCols;


    public AnnotationModelHeaderResolver(Class<?> modelClazz) {
        this.modelClazz = modelClazz;

        resolveHeaders();
    }

    
    private void resolveHeaders() {
        hierarchyHeaders = resolveHeadersFromModelClazz(new AtomicInteger(0), modelClazz, new ArrayList<>(), 5);

        // 拍平
        this.flatHeaders = flatHeaders(hierarchyHeaders);

        // 计算头所在的列号
        calculateHeaderCol();
    }

    /*
     * 根据header的合并单元格数从新计算其列号
     */
    public void calculateHeaderCol() {
        int col = 0;
        int index = 1;
        for(HeaderCell hc : flatHeaders) {
            hc.setCol(col);
            hc.setIndex(index++);
            col += hc.getXspan();
        }
        this.totalCols = col;
    }


    private List<HeaderCell> flatHeaders(List<HeaderCell> headers) {
        List<HeaderCell> result = new ArrayList<>();
        for(HeaderCell hc : headers) {
            if(hc.getSubModelClazz() == null) {
                result.add(hc);
            }else {
                result.addAll(flatHeaders(hc.getSubHeaders()));
            }
        }
        return result;
    }


    private List<HeaderCell> resolveHeadersFromModelClazz(AtomicInteger seq, Class<?> targetModelClazz, List<String> parentPropertyNames,
            int maxDepth) {
        if(maxDepth <= 0) {
            return Collections.emptyList();
        }
        
        int curMaxDepth = --maxDepth; // 递归深度减少

        List<HeaderCell> result = new ArrayList<>();
        Map<String, ExcelCol> excelCols = resolveExcelCols(targetModelClazz);
        excelCols.forEach((fieldName, ec) -> {
            HeaderCell hc = new HeaderCell();
            hc.setId(seq.incrementAndGet());
            hc.setModelClazz(targetModelClazz);
            hc.setIndex(ec.index());
            List<String> propertyNames = new ArrayList<>(parentPropertyNames);
            propertyNames.add(fieldName);
            hc.setPropertyNames(propertyNames);
            hc.setXspan(ec.xspan());
            hc.setYspan(1);
            hc.setVal(ec.name());
            hc.setWidth(ec.width());
            hc.setHeaderCellStyleCustomizer(newInstance(ec.headerCellStyleCustomizer(), DefaultCellStyleCustomizer.getInstance()));
            hc.setValueCellStyleCustomizer(newInstance(ec.valueCellStyleCustomizer(), DefaultCellStyleCustomizer.getInstance()));
            
            if(ec.modelToCellsConverter() != DefaultModelToCellsConverter.class) {
                hc.setModelToCellsConverter(newInstance(ec.modelToCellsConverter(), DefaultModelToCellsConverter.getInstance()));
            }
            hc.setModelToCellsHorizontal(ec.modelToCellsHorizontal());
            
            if(ec.subClass() != Object.class) {
                hc.setSubModelClazz(ec.subClass());
                
                // 需要递归
                List<HeaderCell> subList = resolveHeadersFromModelClazz(seq, ec.subClass(), propertyNames, 
                        Math.min(curMaxDepth, ec.recursiveDepth()));
                hc.setSubHeaders(subList);
            }
            
            result.add(hc);
        });

        return result;
    }

    private <D, DS extends D>  D newInstance(Class<DS> cls, D defaultInstance) {
        if(cls == defaultInstance.getClass()) {
            return defaultInstance; 
        }
        try {
            return cls.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            throw new IllegalStateException("创建实例失败:" + cls, e);
        }
    }


    public static Map<String, ExcelCol> resolveExcelCols(Class<?> clazz){
        Map<String, ExcelCol> result = new LinkedHashMap<>();
        for(Field field : clazz.getDeclaredFields()) {
            ExcelCol[] annos = field.getAnnotationsByType(ExcelCol.class);
            if(annos.length > 0) {
                result.put(field.getName(), annos[0]);
            }
        }

        return result;
    }

    public List<HeaderCell> getHierarchyHeaders() {
        return hierarchyHeaders;
    }

    public List<HeaderCell> getFlatHeaders() {
        return flatHeaders;
    }

    public int getTotalCols() {
        return totalCols;
    }

    @Override
    public String toString() {
        return "AnnotationModelHeaderResolver [modelClazz=" + modelClazz + ", hierarchyHeaders="
                + hierarchyHeaders + ", totalCols=" + totalCols + "]";
    }

}